package main

import "fmt"

// import "time"
/*
并发
并发程序的几条优点
	并发能更客观地表现问题模型；
  并发可以充分利用CPU核心的优势，提高程序的执行效率；
  并发能充分利用CPU与其他硬件设备固有的异步性。

有哪些方式可以实现并发执行呢，并发包含以下几种主流的实现模型
	多进程。
	多进程是在操作系统层面进行并发的基本模式。同时也是开销最大的模式。在Linux平台上，很多工具链正是采用这种模式在工作。比如某个Web服务器，它会有专门
的进程负责网络端口的监听和链接管理，还会有专门的进程负责事务和运算。这种方法的好处在于简单、进程间互不影响，坏处在于系统开销大，因为所有的进程都是由内核管理的。
  多线程。
	多线程在大部分操作系统上都属于系统层面的并发模式，也是使用最多的最有效的一种模式。目前，我们所见的几乎所有工具链都会使用这种模式。它比多进程
的开销小很多，但是其开销依旧比较大，且在高并发模式下，效率会有影响。
  基于回调的非阻塞/异步IO。
	这种架构的诞生实际上来源于多线程模式的危机。在很多高并发服务器开发实践中，使用多线程模式会很快耗尽服务器的内存和CPU资源。而这种模式通过
事件驱动的方式使用异步IO，使服务器持续运转，且尽可能地少用线程，降低开销，它目前在Node.js中得到了很好的实践。但是使用这种模式，编程比多线程要复
杂，因为它把流程做了分割，对于问题本身的反应不够自然。
  协程。
	协程（Coroutine）本质上是一种用户态线程，不需要操作系统来进行抢占式调度，且在真正的实现中寄存于线程中，因此，系统开销极小，可以有效提高线程的任务并发
性，而避免多线程的缺点。使用协程的优点是编程简单，结构清晰；缺点是需要语言的支持，如果不支持，则需要用户在程序中自行实现调度器。目前，原生支持协程的语言还很少。


传统并发模型的缺陷，之后再讲解goroutine并发模型是如何逐一解决这些缺陷的
线程类并发模式在原先的确定性中引入了不确定性，这种不确定性给程序的行为带来了意外和危害，也让程序变得不可控。
线程之间通信只能采用共享内存的方式。为了保证共享内存的有效性，我们采取了很多措施，比如加锁等，来避免死锁或资源竞争。
实践证明，我们很难面面俱到，往往会在工程中遇到各种奇怪的故障和问题。

对线程间共享状态的各种操作都被封装在线程之间传递的消息中，这通常要求：发送消息时
对状态进行复制，并且在消息传递的边界上交出这个状态的所有权。从逻辑上来看，这个操作
与共享内存系统中执行的原子更新操作相同，但从物理上来看则非常不同。由于需要执行复制
操作，所以大多数消息传递的实现在性能上并不优越，但线程中的状态管理工作通常会变得更
为简单。


在CSP系统中，所有的并发操作都是通过独立线程以异步运行的方式来实现的。这些线程必须通过在彼此之间发送消息，
从而向另一个线程请求信息或者将信息提供给另一个线程。
一些语言开始完善消息传递系统，并以此为核心支持并发，比如Erlang。



协程
	执行体是个抽象的概念，比如操作系统自己掌管的进程（process）、进程内的线程（thread）以及进程内的协程（coroutine，也叫轻量级线程）。与
传统的系统级线程和进程相比，协程的最大优势在于其“轻量级”，可以轻松创建上百万个而不会导致系统资源衰竭，而线程和进程通常最多也不能超过1万个。
这也是协程也叫轻量级线程的原因。
	Go 语言在语言级别支持轻量级线程，叫goroutine。Go 语言标准库提供的所有系统调用操作（当然也包括所有同步 IO 操作），
都会出让 CPU 给其他goroutine。这让事情变得非常简单，让轻量级线程的切换管理不依赖于系统的线程和进程，也不依赖于CPU的核心数量。


goroutine
goroutine是Go语言中的轻量级线程实现，由Go运行时（runtime）管理。
*/

func main() {
	fmt.Println("并发操作")
	// 让这个函数并发执行
	// fmt.Println(go Add(11,22))  //syntax error: unexpected go, expecting expression
	// go var sum := Add(11,22)  //syntax error: unexpected var, expecting expression
	// res := go Add(11,22)  // syntax error: unexpected go, expecting expression

	go Add(11, 22) //这次调用就会在一个新的goroutine中并发执行。当被调用
	// 的函数返回时，这个goroutine也自动结束了。需要注意的是，如果这个函数有返回值，那么这个
	// 返回值会被丢弃。

	for i := 0; i < 10; i++ { // for 循环中调用了10次 Add() 函数，它们是并发执行的
		go Add(i, i*10) //此处的Add方法不会输出任何内容
	}
	// Go语言的程序执行机制
	// Go程序从初始化 main package 并执行 main() 函数开始，当 main() 函数返回时，程序退出，
	// 且程序并不等待其他goroutine（非主goroutine）结束。
	// 对于上面的例子，主函数启动了10个goroutine，然后返回，这时程序就退出了，而被启动的
	// 执行 Add(i, i) 的goroutine没有来得及执行，所以程序没有任何输出。

	Goroutine1()
	// 要让主函数等待所有goroutine退出后再返回，如何知道goroutine都退出了呢?这就引出了多个goroutine之间通信的问题。
}

func Goroutine1() {
	fmt.Println("Goroutine1() running.....") //
	for i := 0; i < 10; i++ {                // for 循环中调用了10次 Add() 函数，它们是并发执行的
		go Add(i, i*10) //此处的Add方法输出的内容  比较随机
	}
}

func Add(num1, num2 int) /*int*/ { //使用goroutine调用的函数的返回值被丢弃
	sum := num1 + num2
	fmt.Println(sum)
	// return sum  //返回值也就无意义
}
